﻿/*
BF3Rcon.NET, provides a .NET implementation of BF3's RCON interface.
Copyright (C) 2011 agentwite, Timi, Unseen, AlCapwn

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

You can contact us at http://bf3rcon.codeplex.com/.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Globalization;

namespace System.Net.Battlefield3
{
    /// <summary>
    /// Contains all of the <see cref="Player">Players</see> in the server an <see cref="RconClient"/> is connected to.
    /// </summary>
    public sealed class PlayerCollection : IList<Player>
    {
        const int MaxPlayerCount = 64;

        // this stores players in a way indexed by an int.
        // this is done rather than dict.Values for more flexibility
        //List<Player> playerList = new List<Player>(MaxPlayerCount);

        // this dict will index by player name and contain the same player as in the list
        // this is done to keep indexing by name an O(1) operation.
        Dictionary<string, Player> playerDictionary = new Dictionary<string, Player>(MaxPlayerCount);

        internal PlayerCollection()
        {
            
        }

        /// <summary>
        /// Gets the <see cref="Player"/> at the specified index in the <see cref="PlayerCollection"/>.
        /// </summary>
        /// <param name="index">The index of the <see cref="Player"/>.</param>
        /// <returns>The <see cref="Player"/> at the specified index.</returns>
        public Player this[int index]
        {
            get { return playerDictionary.Values.ElementAt(index); }
            //internal set { PlayerCollection[index] = value; }
        }

        /// <summary>
        /// Gets the <see cref="Player"/> with the matching name.
        /// </summary>
        /// <param name="name">The name of the <see cref="Player"/>.</param>
        /// <returns>A <see cref="Player"/> with a matching name.</returns>
        public Player this[string name]
        {
            get { return playerDictionary[name]; }
            //internal set{}
        }

        internal void Remove(string name)
        {
            if (playerDictionary.ContainsKey(name)) playerDictionary.Remove(name);
        }

        internal void Add(Player player)
        {
            playerDictionary[player.Name] = player;
        }

        /// <summary>
        /// Determines if the <see cref="Player"/> exists by name.
        /// </summary>
        /// <param name="name">The name of the <see cref="Player"/>.</param>
        /// <returns>True if the <see cref="Player"/> is in the <see cref="PlayerCollection"/>.</returns>
        public bool PlayerExists(string name)
        {
            return playerDictionary.ContainsKey(name);
        }

        #region IList<Player> Members

        /// <summary>
        /// Searches for the specified <see cref="Player"/> and returns
        /// the zero-based index where the names match withing the <see cref="PlayerCollection"/>.
        /// </summary>
        /// <param name="item">The <see cref="Player"/> that will be searched for.</param>
        /// <returns>The zero-based index where the specified <see cref="Player">Player's</see> name matched one
        /// in the <see cref="PlayerCollection"/>.</returns>
        public int IndexOf(Player item)
        {
            for (int i = 0; i < playerDictionary.Count; i++)
            {
                if (playerDictionary.Values.ElementAt(i).Equals(item)) return i;
            }

            return -1;
            //return PlayerCollection.IndexOf(item);
        }

        void IList<Player>.Insert(int index, Player item)
        {
            throw new NotSupportedException("PlayerCollection is read-only.");
        }

        void IList<Player>.RemoveAt(int index)
        {
            throw new NotSupportedException("PlayerCollection is read-only.");
        }

        Player IList<Player>.this[int index]
        {
            get
            {
                return playerDictionary.Values.ElementAt(index);
                //return PlayerCollection[index];
            }
            set
            {
                throw new NotSupportedException("PlayerCollection is read-only.");
            }
        }
        #endregion

        #region ICollection<Player> Members

        void ICollection<Player>.Add(Player item)
        {
            throw new NotSupportedException("PlayerCollection is read-only.");
        }

        void ICollection<Player>.Clear()
        {
            throw new NotSupportedException("PlayerCollection is read-only.");
        }

        /// <summary>
        /// Determines whether the specified <see cref="Player"/> has the same name
        /// as another in the <see cref="PlayerCollection"/>.
        /// </summary>
        /// <param name="item">The <see cref="Player"/> to locate in the <see cref="PlayerCollection"/>.</param>
        /// <returns>True if the specified <see cref="Player"/> is in the <see cref="PlayerCollection"/>.</returns>
        public bool Contains(Player item)
        {
            return playerDictionary.ContainsKey(item.Name);
            //return PlayerCollection.Contains(item);
        }

        /// <summary>
        /// Copies the entire <see cref="PlayerCollection"/> to a compatible array, starting at the specified index of the target array.
        /// </summary>
        /// <param name="array">The array that will hold the <see cref="Player">Players</see>.</param>
        /// <param name="arrayIndex">The zero-based index in the <paramref name="array"/> where copying will begin.</param>
        public void CopyTo(Player[] array, int arrayIndex)
        {
            // these were made to make exceptions more specific to PlayerCollection, but getting exceptions
            // from List<Player> is probably safer
            //if (array == null) throw new NullReferenceException("array is a null reference (Nothing in Visual Basic).");
            //if (index < 0) throw new IndexOutOfRangeException("index is less than zero.");
            //if (Count > array.Length - index) throw new ArgumentException("The number of Players in the PlayerCollection is greater than the available space from index to the end of the destination array.");

            playerDictionary.Values.CopyTo(array, arrayIndex);
            //PlayerCollection.CopyTo(array, index);
        }

        /// <summary>
        /// Gets the number of <see cref="Player">Players</see> in the <see cref="PlayerCollection"/>.
        /// </summary>
        /// <value>The number of <see cref="Player">Players</see> in the <see cref="PlayerCollection"/>.</value>
        public int Count
        {
            get { return playerDictionary.Count; }
        }

        // note that even though it's readonly, internal methods will allow the list to be edited

        /// <summary>
        /// Gets whether or not the <see cref="PlayerCollection"/> is read-only.
        /// </summary>
        /// <value>True because only the <see cref="RconClient"/> may modify a <see cref="PlayerCollection"/>.</value>
        public bool IsReadOnly
        {
            get { return true; }
        }

        bool ICollection<Player>.Remove(Player item)
        {
            throw new NotSupportedException("PlayerCollection is read-only.");
        }

        #endregion

        #region IEnumerable<Player> Members
        /// <summary>
        /// Returns an enumerator that iterates through the <see cref="PlayerCollection"/>.
        /// </summary>
        /// <returns>An IEnumerator(Player) that iterates through the <see cref="PlayerCollection"/>.</returns>
        public IEnumerator<Player> GetEnumerator()
        {
            return playerDictionary.Values.GetEnumerator();
            //return PlayerCollection.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members
        /// <summary>
        /// Returns an enumerator that iterates through the <see cref="PlayerCollection"/>.
        /// </summary>
        /// <returns>An <see cref="T:System.Collections.Generic.IEnumerator`1">IEnumerator(Player)</see> that iterates through the <see cref="PlayerCollection"/>.</returns>
        Collections.IEnumerator Collections.IEnumerable.GetEnumerator()
        {
            return playerDictionary.Values.GetEnumerator();
            //return PlayerCollection.GetEnumerator();
        }

        #endregion
    }
}
